"""
表结构定义和记录管理

包含表结构定义、列类型、记录序列化等功能
"""

import struct
import json
from typing import Dict, List, Any, Optional, Union
from enum import Enum
from datetime import datetime


class ColumnType(Enum):
    """列数据类型枚举"""
    INT = "INT"
    VARCHAR = "VARCHAR"
    CHAR = "CHAR"
    FLOAT = "FLOAT"
    BOOLEAN = "BOOLEAN"
    DATE = "DATE"


class Column:
    """列定义"""
    
    def __init__(self, name: str, column_type: ColumnType, length: int = None, 
                 nullable: bool = True, primary_key: bool = False, default_value: Any = None):
        self.name = name
        self.type = column_type
        self.length = length  # 对于VARCHAR/CHAR类型
        self.nullable = nullable
        self.primary_key = primary_key
        self.default_value = default_value
        
        # 验证类型和长度的一致性
        if column_type in [ColumnType.VARCHAR, ColumnType.CHAR] and length is None:
            raise ValueError(f"{column_type.value} type requires length specification")
        if column_type not in [ColumnType.VARCHAR, ColumnType.CHAR] and length is not None:
            raise ValueError(f"{column_type.value} type does not support length specification")
    
    def get_fixed_size(self) -> int:
        """获取列的固定大小（字节数）"""
        if self.type == ColumnType.INT:
            return 4
        elif self.type == ColumnType.FLOAT:
            return 4
        elif self.type == ColumnType.BOOLEAN:
            return 1
        elif self.type == ColumnType.DATE:
            return 8  # 存储为时间戳
        elif self.type == ColumnType.CHAR:
            return self.length
        elif self.type == ColumnType.VARCHAR:
            return 4 + self.length  # 4字节长度 + 最大长度
        else:
            raise ValueError(f"Unknown column type: {self.type}")
    
    def to_dict(self) -> Dict:
        """转换为字典格式"""
        return {
            "name": self.name,
            "type": self.type.value,
            "length": self.length,
            "nullable": self.nullable,
            "primary_key": self.primary_key,
            "default_value": self.default_value
        }
    
    @classmethod
    def from_dict(cls, data: Dict) -> 'Column':
        """从字典创建Column对象"""
        return cls(
            name=data["name"],
            column_type=ColumnType(data["type"]),
            length=data.get("length"),
            nullable=data.get("nullable", True),
            primary_key=data.get("primary_key", False),
            default_value=data.get("default_value")
        )
    
    def __str__(self) -> str:
        type_str = f"{self.type.value}"
        if self.length:
            type_str += f"({self.length})"
        flags = []
        if self.primary_key:
            flags.append("PK")
        if not self.nullable:
            flags.append("NOT NULL")
        flag_str = " " + " ".join(flags) if flags else ""
        return f"{self.name}: {type_str}{flag_str}"


class TableSchema:
    """表结构定义"""
    
    def __init__(self, table_name: str, columns: List[Column]):
        self.table_name = table_name
        self.columns = columns
        self.created_time = datetime.now()
        self.page_ids: List[int] = []  # 数据页ID列表
        self.primary_key_column = None  # 主键列
        self.has_primary_key_index = False  # 是否已创建主键索引
        
        # 验证表结构
        self._validate_schema()
    
    def _validate_schema(self):
        """验证表结构的合法性"""
        if not self.columns:
            raise ValueError("Table must have at least one column")
        
        # 检查列名唯一性
        column_names = [col.name for col in self.columns]
        if len(column_names) != len(set(column_names)):
            raise ValueError("Column names must be unique")
        
        # 检查主键唯一性
        primary_keys = [col for col in self.columns if col.primary_key]
        if len(primary_keys) > 1:
            raise ValueError("Table can have at most one primary key")
        elif len(primary_keys) == 1:
            self.primary_key_column = primary_keys[0]
            # 主键列不能为NULL
            self.primary_key_column.nullable = False
    
    def get_column(self, name: str) -> Optional[Column]:
        """根据名称获取列定义"""
        for col in self.columns:
            if col.name == name:
                return col
        return None
    
    def get_column_names(self) -> List[str]:
        """获取所有列名"""
        return [col.name for col in self.columns]
    
    def get_primary_key(self) -> Optional[Column]:
        """获取主键列"""
        return self.primary_key_column
    
    def has_primary_key(self) -> bool:
        """检查是否有主键"""
        return self.primary_key_column is not None
    
    def get_primary_key_type(self) -> str:
        """获取主键的数据类型字符串（用于索引）"""
        if self.primary_key_column is None:
            return None
        
        if self.primary_key_column.type == ColumnType.INT:
            return 'int'
        elif self.primary_key_column.type == ColumnType.FLOAT:
            return 'float'
        elif self.primary_key_column.type in [ColumnType.VARCHAR, ColumnType.CHAR]:
            return 'string'
        else:
            return 'string'  # 其他类型默认按字符串处理
    
    def get_record_size(self) -> int:
        """获取记录的固定大小（字节数）"""
        size = 4  # 记录头（4字节：记录长度）
        for col in self.columns:
            if col.nullable:
                size += 1  # NULL标记位
            size += col.get_fixed_size()
        return size
    
    def to_dict(self) -> Dict:
        """转换为字典格式"""
        return {
            "table_name": self.table_name,
            "columns": [col.to_dict() for col in self.columns],
            "created_time": self.created_time.isoformat(),
            "page_ids": self.page_ids
        }
    
    @classmethod
    def from_dict(cls, data: Dict) -> 'TableSchema':
        """从字典创建TableSchema对象"""
        columns = [Column.from_dict(col_data) for col_data in data["columns"]]
        schema = cls(data["table_name"], columns)
        schema.created_time = datetime.fromisoformat(data["created_time"])
        schema.page_ids = data.get("page_ids", [])
        return schema
    
    def __str__(self) -> str:
        lines = [f"Table: {self.table_name}"]
        lines.extend([f"  {col}" for col in self.columns])
        lines.append(f"Pages: {self.page_ids}")
        return "\n".join(lines)


class Record:
    """记录（行）对象"""
    
    def __init__(self, schema: TableSchema, values: Dict[str, Any] = None):
        self.schema = schema
        self.values: Dict[str, Any] = values or {}
        self.record_id: Optional[int] = None  # 记录ID（在页面中的位置）
    
    def set_value(self, column_name: str, value: Any):
        """设置列值"""
        column = self.schema.get_column(column_name)
        if not column:
            raise ValueError(f"Column '{column_name}' not found in table '{self.schema.table_name}'")
        
        # 类型验证和转换
        validated_value = self._validate_and_convert_value(column, value)
        self.values[column_name] = validated_value
    
    def get_value(self, column_name: str) -> Any:
        """获取列值"""
        return self.values.get(column_name)
    
    def _validate_and_convert_value(self, column: Column, value: Any) -> Any:
        """验证和转换值的类型"""
        if value is None:
            if not column.nullable:
                raise ValueError(f"Column '{column.name}' cannot be NULL")
            return None
        
        if column.type == ColumnType.INT:
            return int(value)
        elif column.type == ColumnType.FLOAT:
            return float(value)
        elif column.type == ColumnType.BOOLEAN:
            return bool(value)
        elif column.type == ColumnType.VARCHAR:
            str_value = str(value)
            if len(str_value) > column.length:
                raise ValueError(f"VARCHAR value too long for column '{column.name}' (max {column.length})")
            return str_value
        elif column.type == ColumnType.CHAR:
            str_value = str(value)
            if len(str_value) > column.length:
                raise ValueError(f"CHAR value too long for column '{column.name}' (max {column.length})")
            return str_value.ljust(column.length)  # 右填充空格
        elif column.type == ColumnType.DATE:
            if isinstance(value, datetime):
                return value
            elif isinstance(value, str):
                return datetime.fromisoformat(value)
            else:
                raise ValueError(f"Invalid date value for column '{column.name}'")
        
        return value
    
    def serialize(self) -> bytes:
        """序列化记录为字节数据"""
        data = bytearray()
        
        # 先序列化数据，稍后填充实际长度
        data.extend(struct.pack('I', 0))  # 占位符，稍后替换
        
        # 序列化各列数据
        for column in self.schema.columns:
            value = self.values.get(column.name)
            
            # NULL标记位（如果列可空）
            if column.nullable:
                is_null = value is None
                data.extend(struct.pack('B', 1 if is_null else 0))
                if is_null:
                    # 如果是NULL，填充默认值占位
                    data.extend(bytes(column.get_fixed_size() - (4 if column.type == ColumnType.VARCHAR else 0)))
                    continue
            
            # 序列化具体值
            if column.type == ColumnType.INT:
                data.extend(struct.pack('i', value))
            elif column.type == ColumnType.FLOAT:
                data.extend(struct.pack('f', value))
            elif column.type == ColumnType.BOOLEAN:
                data.extend(struct.pack('B', 1 if value else 0))
            elif column.type == ColumnType.DATE:
                timestamp = value.timestamp()
                data.extend(struct.pack('d', timestamp))
            elif column.type == ColumnType.CHAR:
                char_bytes = value.encode('utf-8')[:column.length]
                char_bytes = char_bytes.ljust(column.length, b'\x00')
                data.extend(char_bytes)
            elif column.type == ColumnType.VARCHAR:
                varchar_bytes = str(value).encode('utf-8')
                actual_length = min(len(varchar_bytes), column.length)
                data.extend(struct.pack('I', actual_length))
                data.extend(varchar_bytes[:actual_length])
                # 填充剩余空间
                data.extend(bytes(column.length - actual_length))
        
        # 更新记录头中的实际长度
        actual_length = len(data)
        struct.pack_into('I', data, 0, actual_length)
        
        return bytes(data)
    
    @classmethod
    def deserialize(cls, schema: TableSchema, data: bytes, offset: int = 0) -> 'Record':
        """从字节数据反序列化记录"""
        record = cls(schema)
        pos = offset
        
        # 读取记录头
        record_length = struct.unpack('I', data[pos:pos+4])[0]
        pos += 4
        
        # 反序列化各列数据
        for column in schema.columns:
            # 读取NULL标记位
            if column.nullable:
                is_null = struct.unpack('B', data[pos:pos+1])[0] == 1
                pos += 1
                if is_null:
                    record.values[column.name] = None
                    # 跳过占位数据
                    pos += column.get_fixed_size() - (4 if column.type == ColumnType.VARCHAR else 0)
                    continue
            
            # 反序列化具体值
            if column.type == ColumnType.INT:
                value = struct.unpack('i', data[pos:pos+4])[0]
                pos += 4
            elif column.type == ColumnType.FLOAT:
                value = struct.unpack('f', data[pos:pos+4])[0]
                pos += 4
            elif column.type == ColumnType.BOOLEAN:
                value = struct.unpack('B', data[pos:pos+1])[0] == 1
                pos += 1
            elif column.type == ColumnType.DATE:
                timestamp = struct.unpack('d', data[pos:pos+8])[0]
                value = datetime.fromtimestamp(timestamp)
                pos += 8
            elif column.type == ColumnType.CHAR:
                char_bytes = data[pos:pos+column.length]
                value = char_bytes.rstrip(b'\x00').decode('utf-8').rstrip()  # 移除右侧空格
                pos += column.length
            elif column.type == ColumnType.VARCHAR:
                actual_length = struct.unpack('I', data[pos:pos+4])[0]
                pos += 4
                varchar_bytes = data[pos:pos+actual_length]
                value = varchar_bytes.decode('utf-8')
                pos += actual_length
                # 跳过填充部分
                pos += (column.length - actual_length)
            
            record.values[column.name] = value
        
        return record
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典格式"""
        result = {}
        for column in self.schema.columns:
            value = self.values.get(column.name)
            if isinstance(value, datetime):
                result[column.name] = value.isoformat()
            else:
                result[column.name] = value
        return result
    
    def __str__(self) -> str:
        values_str = ", ".join([f"{k}={v}" for k, v in self.values.items()])
        return f"Record({values_str})"
